//
//  Shape.hpp
//  RayTracer
//
//  Created by 俞云康 on 2/15/16.
//  Copyright © 2016 yushroom. All rights reserved.
//

#ifndef Shape_hpp
#define Shape_hpp

#include "Math.hpp"
#include "Ray.hpp"
#include "HitInfo.hpp"
#include "Sample.hpp"
#include "Transform.hpp"
#include "AABB.hpp"
#include <string>
#include <memory>

class Params;
class AreaLight;

class Shape {
public:
    std::string  name;
    std::shared_ptr<Transform> transform;
    AABB boundingBox;
    
    Shape(const std::string& name, const std::shared_ptr<Transform>& transform)
        : name(name), transform(transform) {}
    
    static shared_ptr<Shape> create(const Params& params);
    
    virtual ~Shape() {};
    
    virtual bool intersect(const Ray& r, HitInfo* hit) const = 0;
    virtual bool intersect(const Ray& r) const = 0; // fast version
    
    // methods for area lights
    
    virtual float area() const { return 0; }    // for area light, return 0 means it can not be used for area light
    
    // default implementation
    virtual float pdf(const Position& p) const {
        Assert(area() > 0);
        return 1.f / area();
    }
    
    // see PBRT p717
    virtual float pdf(const Position& p, const Direction& wi) const {
        Ray ray(p + wi*EPS, wi);
        HitInfo hit;
        if (!intersect(ray, &hit)) {
            return 0;
        }
        // since hit.normal and -wi are normalized, cos\theta_o = AbsDot(hit.normal, -wi)
        float pdf = DistanceSquared(p, ray.at(ray.maxt)) / (AbsDot(hit.normal, -wi) * area());
		if (std::isinf(pdf)) pdf = 0.f;
		return pdf;
    }
    
    virtual Position sample(const Sample& sample, Normal* shape_normal) const {
        error("Unimplemented Shape::sample method called");
        return Position();
    }
    
    // default implementation
    virtual Position sample(const Position& p, const Sample& sample, Normal* shape_normal) const {
        return this->sample(sample, shape_normal);
    }
};

#endif /* Shape_hpp */
